module ActiveBackground
  # ActiveBackground server is responsible for setting up a DRb server
  # with a ActiveBackground::Manager instance (Singleton).
  # 
  # == Creation
  # 
  # ActiveBackground::Server uses a ActiveBackground:Manager instance
  # to start a DRb server based on the configuration file. It is also
  # possible to pass the arguments by the ActiveBackground::Server.start
  # method. This will overwrite the settings in the configuration file
  # located in ether in the rails config dir (if installed as rails plugin)
  # or in the config directory of ActiveBackground.
  # 
  # See ActiveBackground::Configuration for more information
  # 
  # The following example starts the DRB server:
  # 
  #   ActiveBackground::Server.start()
  # 
  # == Termination
  # 
  # ActiveBackground::Server is shutdown by killing the proces that is
  # started by the ActiveBackground::Server.start() method. The server
  # is given chance to cleanup the threads (and store queued processes
  # in dumpfile). This file can be loaded again when starting up the
  # server.
  # 
  #   ActiveBackground::Server.stop()
  # 
  class Server
    
    @@defaults = {
                  :host => '127.0.0.1',
                  :port => 9595,
                  :config_file => 'active_background.yml',
                  :logging => true,
                  :background => false,
                  :rails => false
                  }
    
    # Start the Server
    # 
    # The server uses the ActiveBackground::Base.defaults to start the
    # server. However configuration options can be overwriten by using
    # the config/active_background.yml file. This starts the DRb service
    # with a ActiveBackground::Manager instance (Singleton). And closes
    # with <tt>DRb.thread.join</tt> that runs till the proces is killed.
    # 
    # * <tt>:host</tt>: the host where the ActiveBackground::Server must be setup on
    # * <tt>:port</tt>: the port where the ActiveBackground::Server must be setup on
    # * <tt>:config_file</tt>: the configuration file that needs to be read (options pased will not be overwritten)
    # * <tt>:logging</tt>: you can enable or disable logging with true or false
    # * <tt>:background</tt>: you can enable or disable running in background with true or false
    # * <tt>:rails</tt>: you can enable or disable loading rails with true or false
    # 
    def self.start(options={})
      options = options.merge!({:server => :master})
      begin
        # load configuration from rails config file,
        # or load file from local config file
        # if both don't exist default to internal defaults
        config = Configuration.instance
        server_options = {:host => config.server(options[:server]).host, :port => config.server(options[:server]).port}
                
        # the uri where we want to make our DRb available
        uri = "druby://#{server_options[:host]}:#{server_options[:port]}"
        
        # Start up DRb with URI and instance of ActiveBackground::Manager
        manager = ActiveBackground::Manager.new
        manager.config=config
        manager.name(options[:server])
        # preserv the object localy for 10 minutes
        idconf = DRb::TimerIdConv.new
        DRb::DRbServer.default_id_conv(idconf)
        # start the server
        server = DRb::DRbServer.new(uri, manager)
        
        # Show some statistics
        self.info()
        # Write a file with the Process id so we can kill it
        self.write()
        
        manager.wait
        
        server.stop_service
        
        # DRb.thread.join
      rescue Exception => e
        logger = config.logger
        logger.error("\n******************************************\n" +
                      "Exception: #{e.to_s} - (#{e.class})\n" +
                      "Server: Server is already running or failed to run\n" +
                      "----------------BACKTRACE-----------------\n" +
                                e.backtrace.join("\n") + "\n" +
                      "-------------------END--------------------\n" +
                      "\n******************************************\n" )
      end
    end
    
    def self.stop
      config = Configuration.instance
      
      # load configuration from rails config file,
      # or load file from local config file
      # if both don't exist default to internal defaults
      begin
        begin # try to load rails
          require File.dirname(__FILE__) + "/../../../../config/boot.rb"
          options = @@defaults.merge(config.load("#{RAILS_ROOT}/config/#{@@defaults[:config_file]}"))
        rescue Exception => e # loading of rails failed
          options = @@defaults.merge(config.load(File.dirname(__FILE__) + "/../config/#{@@defaults[:config_file]}"))
        end
      rescue Exception => e # loading of local file failed
        options = @@defaults
      end
      
      # the uri where we want to make our DRb available
      uri = "druby://#{options[:host]}:#{options[:port]}"
      
      begin
        manager = DRbObject.new(nil, uri)
        while manager.alive?
          manager.terminate unless manager.terminated?
          sleep 0.5
          config.logger.info('Server: waiting for server to shutdown')
          manager = DRbObject.new(nil, uri)
        end
      rescue
        config.logger.info('Server: terminated')
      end
    end
    
    def self.run_in_background
      self.start_windows_service
    end
    
    def self.quick_start_windows
      begin
        self.delete_windows_service
        self.install_windows_service
        self.restart_windows_service
      rescue Exception => e
        puts e.to_s + "\n" + e.backtrace.join("\n")
      end
    end
    
    def self.restart_windows_service
      self.stop_windows_service
      self.start_windows_service
    end
    
    def self.start_windows_service
      self.require_windows_libs
      
      # we can only start a service if it exists (installed)
      if Service.exists?("ActiveBackground")     
        s = Service.status("ActiveBackground")
        # check if the service is not already running
        unless s.current_state == 'running'
          Service.start("ActiveBackground")
          # wait till the service has started before giving back control
          while s.current_state != 'running'
            puts " * Service starting wait please. Current status: " + s.current_state
            sleep 2
            s = Service.status("ActiveBackground")
          end
          puts "Service is started."
        else # inform if the service was already started
          puts "service has already been started"
        end
      else # service does not exist
        puts "Service does not exist please run rake active_background:install_windows_service"
      end
    end
    
    def self.stop_windows_service
      self.require_windows_libs
      
      # we can only start a service if it exists (installed)
      if Service.exists?("ActiveBackground")
        s = Service.status("ActiveBackground")
        # check if the service is not already stopped
        unless s.current_state == 'stopped'
          # self.stop # allow the server to terminate and save its jobs          
          Service.stop("ActiveBackground")
          # wait till the service has stopped before giving back control
          while s.current_state != 'stopped'
            puts " * Service stoping wait please. Current status: " + s.current_state
            sleep 2
            s = Service.status("ActiveBackground")
          end
        end
      puts "Service is stopped."
      else
        puts "Service does not exist please run rake active_background:install_windows_service"
      end
    end
    
    def self.install_windows_service
      self.require_windows_libs
      
      if !Service.exists?("ActiveBackground")     
        svc = Service.new
        svc.create_service do |s|
          s.service_name = "ActiveBackground"
          s.display_name = "Active Background"
          s.binary_path_name = "ruby " + File.expand_path(File.dirname(__FILE__) + "/../../script/windows_service.rb")
          puts s.binary_path_name
        end
        svc.close
        puts "Registered Service"
      else
        puts "Service already registered"
      end
    end
    
    def self.delete_windows_service
      self.require_windows_libs
      
      if Service.exists?("ActiveBackground")
        begin
          self.stop_windows_service
          puts "Trying to delete the service"
          Service.delete("ActiveBackground")
          while Service.exists?("ActiveBackground")
            puts "Trying to delete service."
            sleep 2
          end
          puts 'Service has been removed'
        rescue Exception => e
          puts e.to_s
        end
      end
    end
    
    def defaults
      @@defaults
    end
    
    def self.require_windows_libs
      require 'rubygems'
      require 'win32/service'
      include Win32
    end
    
    private
      # output some statistics
      def self.info
        Configuration.instance.logger.debug "DRb URI: #{DRb.uri}"
        Configuration.instance.logger.debug "DRb Pid: #{Process.pid}"
      end
      
      # saving the pid so we can kill the proces later on
      def self.write
        File.open( File.dirname(__FILE__) + "/../../tmp/ActiveBackground.pid", 'w+' ) { |f| f.write(Process.pid) }
      end
      
      # reading the pid so we can kill the proces later on
      def self.read
        line = nil
        File.open(File.dirname(__FILE__) + "/../../tmp/ActiveBackground.pid") do |file|
          line = file.gets
        end
        
        return line.to_i
      end
  end
end